package it.webrules.deploymanager.core.dy.repos.nexus;

import it.webrules.deploymanager.core.HTTPConnection;
import it.webrules.deploymanager.core.dy.Versioning;
import it.webrules.deploymanager.core.dy.VersioningUtils;
import it.webrules.deploymanager.core.dy.build.BuildHistory;
import it.webrules.deploymanager.core.dy.build.BuildHistory.Build;
import it.webrules.deploymanager.core.dy.repos.RepositoryConnection;
import it.webrules.deploymanager.core.dy.repos.RepositoryException;
import it.webrules.xsd.dmc.config.v1.Nexus;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class NexusConnection implements RepositoryConnection {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	private XPath xpath;
	private DocumentBuilder builder;
	private HTTPConnection httpConnection;

	private String groupid;
	private String artifactid;

	public NexusConnection(Nexus nexus) throws RepositoryException {
		this(nexus.getUrl().getValue(), nexus.getGroupid(), nexus.getArtifactid(), nexus.getUrl().getUsername(), nexus.getUrl()
				.getPassword());
	}

	public NexusConnection(String serverUrl, String groupid, String artifactid, String username, String password)
			throws RepositoryException {
		this(groupid, artifactid, new it.webrules.deploymanager.core.dy.HTTPConn(serverUrl, username, password));
	}

	public NexusConnection(String groupid, String artifactid, HTTPConnection httpConnection) throws RepositoryException {

		try {

			XPathFactory xfactory = XPathFactory.newInstance();
			xpath = xfactory.newXPath();

			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			this.builder = factory.newDocumentBuilder();

			this.groupid = groupid;
			this.artifactid = artifactid;
			this.httpConnection = httpConnection;

		} catch (ParserConfigurationException e) {
			throw new RepositoryException("Errore creating connection to build system!", e);
		}
	}

	private String getArtifactUrl() {
		return groupid.replace(".", "/") + "/" + artifactid.replace(".", "/");
	}

	private Document getMetadata() throws SAXException, IOException {
		return builder.parse(httpConnection.getRemoteFile(getArtifactUrl() + "/maven-metadata.xml"));
	}

	private Document getPom(String release) throws SAXException, IOException {
		String pom = artifactid + "-" + release + ".pom";
		InputStream pomFile = httpConnection.getRemoteFile(getArtifactUrl() + "/" + release + "/" + pom);
		return builder.parse(pomFile);
	}

	@Override
	public BuildHistory getBuildHistory() throws RepositoryException {

		BuildHistory buildHistory = new BuildHistory();

		try {

			Document metadataDocument = getMetadata();

			String rootVer = "/metadata/versioning/";
			XPathExpression exprLatest = xpath.compile(rootVer + "latest");
			XPathExpression exprRelease = xpath.compile(rootVer + "release");
			XPathExpression exprVers = xpath.compile(rootVer + "versions/version");

			String latest = (String) exprLatest.evaluate(metadataDocument, XPathConstants.STRING);
			String release = (String) exprRelease.evaluate(metadataDocument, XPathConstants.STRING);

			NodeList list = (NodeList) exprVers.evaluate(metadataDocument, XPathConstants.NODESET);

			for (int i = 0; i < list.getLength(); i++) {
				Node node = list.item(i);
				String ver = node.getTextContent();
				Build build = buildHistory.addBuild(ver, true);

				if (ver.equals(latest))
					build.addFlag("Latest");

				if (ver.equals(release))
					build.addFlag("Release");

			}

		} catch (Exception e) {
			throw new RepositoryException("Error listing builds.", e);
		}

		return buildHistory;

	}

	/**
	 * Search the release marked as release. 
	 * If not found, search for latest one.
	 * If not found again, return the last version number in maven-metadata.
	 * If there aren't versioning, return "1.0.0-SNAPSHOT".
	 * 
	 * @return
	 * @throws RepositoryException
	 */
	private String getReleaseVersion() throws RepositoryException {

		BuildHistory buildHistory = getBuildHistory();
		List<Build> builds = buildHistory.getBuilds();

		if (builds.size() == 0)
			throw new RepositoryException("No releases found on nexus!");

		for (Build build : builds) {
			if (build.flasg.contains("Release"))
				return build.buildNumber;
		}

		for (Build build : builds) {
			if (build.flasg.contains("Latest"))
				return build.buildNumber;
		}

		if (builds.size() == 0)
			return "1.0.0";

		return builds.get(builds.size() - 1).buildNumber;
	}

	@Override
	public InputStream getArtifact(String release) throws RepositoryException {

		if (release == null)
			release = getReleaseVersion();

		logger.debug("Getting {} release of artifact.", release);

		try {

			Document pom = getPom(release);
			XPathExpression exprPkg = xpath.compile("/project/packaging");
			String packaging = (String) exprPkg.evaluate(pom, XPathConstants.STRING);

			String artifactFile = artifactid + "-" + release + "." + packaging;
			return httpConnection.getRemoteFile(getArtifactUrl() + "/" + release + "/" + artifactFile);

		} catch (Exception e) {
			throw new RepositoryException(e.getMessage(), e);
		}

	}

	@Override
	public Versioning getNextVersions() throws RepositoryException {

		Versioning versioning = new Versioning();

		String releaseVersion = getReleaseVersion();
		releaseVersion = VersioningUtils.updateVersion(releaseVersion);
		versioning.setRelease(releaseVersion);
		String developmentVersion = VersioningUtils.updateVersion(releaseVersion);
		versioning.setDevelop(developmentVersion + "-SNAPSHOT");

		return versioning;

	}

}
